#include <CoOS.h>
#include "ir_remo_ctrl.h"
#include "ir_decode.h"

#include "Driver\DrvGPIO.h"
#include "Driver\DrvPWM.h"

//#define LOG_DEBUG(x)	usb_printf x
#define LOG_DEBUG(x)	

S_DRVPWM_TIME_DATA_T gPt;

#define IR_QUE_SIZE		(32)

OS_EventID ir_que;
void * ir_que_container[IR_QUE_SIZE];
OS_STK   task_ir_decode_stk[TASK_STK_SIZE];	 	/*!< Stack of 'task_init' task.	  */

void CaptureConfig(void);

#define IR_BUF_SIZE (16)
uint8_t g_ir_buf[IR_BUF_SIZE];
uint8_t g_ir_buf_len;

typedef enum _IR_DECODE_STATUS
{
	IR_STANDBY,
	IR_LEADIN_LOW,
	IR_LEADIN_HI,
	IR_DATA_LOW,
	IR_DATA_HI,
	IR_ERROR,
}	IR_DECODE_STATUS;


enum _LAMP_STATUS
{
	LAMP_OFF,
	LAMP_ON1,
	LAMP_ON2,
};

typedef enum _LAMP_STATUS 	LAMP_STATUS;
LAMP_STATUS		g_lamp_status = LAMP_ON2;

// macro of check x in range t~t+delta
#define IN_RANGE(t, x)	((t-PW_DELTA < x) && (x < t+PW_DELTA))
#define CHECK_PULSE_LO(t, p, x) ((p == 0) && IN_RANGE(t, x))	
#define CHECK_PULSE_HI(t, p, x) ((p != 0) && IN_RANGE(t, x))	

#define PW_DELTA		20
#define PW_LEADIN_LOW	0x233
#define PW_LEADIN_HI	0x116
#define PW_DATA_LOW		0x27
#define PW_DATA_HI_1	0x67
#define PW_DATA_HI_0	0x22
#define PW_REPEAT		0x8C

// return value: ir length,	0xFFFF: time out
// po 0: low pulse, 1: high pulse
uint16_t ReadIr(OS_EventID ir_que, int * po, U32 timeout)
{
	StatusType err = E_OK;	
	uint32_t ir_sig = (uint32_t)(CoPendQueueMail(ir_que, timeout, &err));

	if (err == E_TIMEOUT)
	{
//		LOG_DEBUG(("*TIMEOUT, \n\r"));
	 	return 0xFFFF;
	}
	else if (err == E_OK)
	{
		// for test
//		LOG_DEBUG(("*%08X, \n\r", ir_sig));
		*po = ((ir_sig & 0x80000000) != 0);
		return (ir_sig & 0x7FFFFFFF);
	}
	else
	{	//<TODO> error handling.
		LOG_DEBUG(("* OS ERROR, \n\r"));
	}
	return 0;
}

void OnError(OS_EventID ir_que)
{
	uint16_t ir_count = 0;
	int ir_low_hi = 0;

	LOG_DEBUG(("decode error!\n\r"));
	DrvGPIO_ClrBit(E_GPB, 0);   		// LED on

	while (ir_count < 0xFFFF)
	{
		ir_count = ReadIr(ir_que, &ir_low_hi, 20);
	}
//	CoTickDelay(50);
	DrvGPIO_SetBit(E_GPB, 0);   		// LED off
}

void OnButtonPress(void)
{
	// change lamp status
	switch (g_lamp_status)
	{
	case LAMP_OFF:
		g_lamp_status = LAMP_ON1;
		DrvGPIO_SetBit(E_GPC, 6);
		DrvGPIO_ClrBit(E_GPC, 7);
		break;
		
	case LAMP_ON1:
		g_lamp_status = LAMP_ON2;
		DrvGPIO_SetBit(E_GPC, 6);
		DrvGPIO_SetBit(E_GPC, 7);
		break;

	case LAMP_ON2:
		g_lamp_status = LAMP_OFF;
		DrvGPIO_ClrBit(E_GPC, 6);
		DrvGPIO_ClrBit(E_GPC, 7);
		break;
	}
}

void OnIrDecode(uint8_t * buf, uint8_t buf_len)
{
	int ii;
	// output data
	LOG_DEBUG(("received data: "));

	DrvGPIO_ClrBit(E_GPB, 0);
	CoTickDelay(5);
	DrvGPIO_SetBit(E_GPB, 0);

	for (ii = 0; ii < g_ir_buf_len; ++ii)
	{
		LOG_DEBUG(("%02X ", g_ir_buf[ii]));
	}
	LOG_DEBUG(("\n\r"));

	if ( (buf_len==4) && (buf[0]==0x82) && (buf[1]==0x6D) && (buf[2]==0x9A) && (buf[3]==0x65) )
	{
		OnButtonPress();
	} 
}

void ReceiveIrData(OS_EventID ir_que)
{
//-- Definition	
	int ir_low_hi = 0;
	uint16_t ir_count = 0;
	uint8_t bit_mask;
	uint8_t val;
//	int ii;

//-- Code
	// waiting for lead-in low pulse
	ir_count = ReadIr(ir_que, &ir_low_hi, 10);
	if ( ! CHECK_PULSE_LO(PW_LEADIN_LOW, ir_low_hi, ir_count) )
	{	// <ERR HANDLE> ir
		LOG_DEBUG(("LEADIN-LOW, count=0x%04X, po=%d", ir_count, ir_low_hi));
		OnError(ir_que);
		return;
	}
//	LOG_DEBUG(("LEADIN-LOW\n\r"));

	// waiting for lead-in low pulse
	ir_count = ReadIr(ir_que, &ir_low_hi, 10);
	if ( CHECK_PULSE_HI(PW_LEADIN_HI, ir_low_hi, ir_count) )
	{
//		LOG_DEBUG(("LEADIN-HI\n\r"));
	}
	else if ( CHECK_PULSE_HI(PW_REPEAT, ir_low_hi, ir_count) )
	{
//		LOG_DEBUG(("REPEAT\n\r"));
	}
	else
	{
//		LOG_DEBUG(("LEADIN-HI - error\n\r"));
		LOG_DEBUG(("LEADIN-HI, count=0x%04X, po=%d", ir_count, ir_low_hi));
		OnError(ir_que);
		return;
	}

	g_ir_buf_len = 0;
	while (1)
	{
		val = 0;
		for (bit_mask = 0x01; bit_mask != 0; bit_mask <<= 1)
		{
			// waiting data low pulse	
 			ir_count = ReadIr(ir_que, &ir_low_hi, 10);
			if ( ! CHECK_PULSE_LO(PW_DATA_LOW, ir_low_hi, ir_count) )
			{
				LOG_DEBUG(("DATA-LOW, count=0x%04X, po=%d", ir_count, ir_low_hi));
				OnError(ir_que);
				return;
			}

			// waiting data hi pulse
 			ir_count = ReadIr(ir_que, &ir_low_hi, 2);
			if ( CHECK_PULSE_HI(PW_DATA_HI_1, ir_low_hi, ir_count) )
			{
				val |= bit_mask;
			}
			else if ( CHECK_PULSE_HI(PW_DATA_HI_0, ir_low_hi, ir_count) )
			{
			}
			else if (ir_count == 0xFFFF)	
			{	// time out
				break;
			}
			else
			{
				LOG_DEBUG(("DATA-HI, count=0x%04X, po=%d", ir_count, ir_low_hi));
				OnError(ir_que);
				return;
			}
		}
		if (ir_count == 0xFFFF) break;
//		LOG_DEBUG(("data[%d]=0x%02X\n\r", g_ir_buf_len, val));
		g_ir_buf[g_ir_buf_len] = val;
		g_ir_buf_len ++;
		if (g_ir_buf_len >= IR_BUF_SIZE) break;
	}
	OnIrDecode(g_ir_buf, g_ir_buf_len);
}


void task_ir_decode(void * pdata)
{
//-- Definition	
	int ii;
//-- Code
	// init lamp
	g_lamp_status = LAMP_ON2;
	DrvGPIO_SetBit(E_GPC, 6);
	DrvGPIO_SetBit(E_GPC, 7);

	ir_que = CoCreateQueue(ir_que_container, IR_QUE_SIZE, EVENT_SORT_TYPE_FIFO);
	if (ir_que == E_CREATE_FAIL)
	{	//<TODO> error handling.
	}
	UNLOCKREG();
	CaptureConfig();
	LOCKREG();

	for (ii=0; ii<5; ++ii)
	{
		DrvGPIO_ClrBit(E_GPB, 0);
		CoTickDelay(20);
		DrvGPIO_SetBit(E_GPB, 0);
		CoTickDelay(20);
	}

	while (1)
	{
		// stand by, waiting for high pulse (failing edge), without timeout
		int ir_low_hi = 0;
		uint16_t ir_count = 0;
		
		LOG_DEBUG(("ready\n\r"));
		ir_count = ReadIr(ir_que, &ir_low_hi, 0);
		if (ir_count == 0 || ir_count == 0xFFFF)
		{	//<ERR HANDLE>
		}
		if (ir_low_hi != 0)
		{
//			LOG_DEBUG(("STANDBY\n\r"));
			LOG_DEBUG(("decoding...\n\r"));
			ReceiveIrData(ir_que);
		}
	}

	CoDelQueue(ir_que, OPT_DEL_ANYWAY);
	CoExitTask();
}

void CaptureIrqHandle(void)
{	// !! This function is run in IRQ
//-- Declaration
	uint32_t count=0;
//	int low_hi = 0;	// 0: rising (low puls) 1: failing (hi puls)
	StatusType err;


//-- Code
    if (DrvPWM_GetCaptureIntStatus(DRVPWM_CAP0, DRVPWM_CAP_FALLING_FLAG) == 1)
	{	// detected failing edeg, means high puls
		count = 0x80000000 | (0x8000 - DrvPWM_GetFallingCounter(DRVPWM_CAP0) );
	    /* Clear the Capture Interrupt Flag (Time A) */
	    DrvPWM_ClearCaptureIntStatus(DRVPWM_CAP0, DRVPWM_CAP_FALLING_FLAG);
//		low_hi = 1;
	}
	else if (DrvPWM_GetCaptureIntStatus(DRVPWM_CAP0, DRVPWM_CAP_RISING_FLAG) == 1)
	{	// detected	rising edge, means low puls
		count = (0x8000 - DrvPWM_GetRisingCounter(DRVPWM_CAP0) );
	    /* Clear the Capture Interrupt Flag (Time A) */
	    DrvPWM_ClearCaptureIntStatus(DRVPWM_CAP0, DRVPWM_CAP_RISING_FLAG);
//		low_hi = 0;
	}

	CoEnterISR();
	err = isr_PostQueueMail(ir_que, (void*)count);
	if (err != E_OK)
	{	//<TODO> error handling.
	}
	CoExitISR();

/*
	switch (g_ir_status)
	{
	case IR_LEADIN_LOW:
		if ( (0 == low_hi) && (550 < count) && (count < 590) )
		{
			printf("low l: %d,i: %d\r\n", g_lamp_status, g_ir_status);
			g_ir_status = IR_LEADIN_HI;
		}
		break;

	case IR_LEADIN_HI:
		if ( (1 == low_hi) && (270 < count) && (count < 290) )
		{
			printf("hi l: %d,i: %d\r\n", g_lamp_status, g_ir_status);
			// Disable capture
			DrvPWM_SetTimerIO(DRVPWM_CAP0, 0); 
			DrvGPIO_SetBit(E_GPB, 0);

			// change lamp status
			switch (g_lamp_status)
			{
			case LAMP_OFF:
				g_lamp_status = LAMP_ON1;
				DrvGPIO_SetBit(E_GPC, 6);
				DrvGPIO_ClrBit(E_GPC, 7);
				break;
				
			case LAMP_ON1:
				g_lamp_status = LAMP_ON2;
				DrvGPIO_SetBit(E_GPC, 6);
				DrvGPIO_SetBit(E_GPC, 7);
				break;

			case LAMP_ON2:
				g_lamp_status = LAMP_OFF;
				DrvGPIO_ClrBit(E_GPC, 6);
				DrvGPIO_ClrBit(E_GPC, 7);
				break;
			}
			g_ir_status = IR_STAND_BY;
		}
		break;

	case IR_STAND_BY:
		printf("stb l: %d,i: %d\r\n", g_lamp_status, g_ir_status);
		break;
	}
*/
}

void CaptureConfig()
{
//	DrvGPIO_Open(E_GPB, 0,  E_IO_OUTPUT);	// LED on board
//	DrvGPIO_Open(E_GPC, 6,  E_IO_OUTPUT);	// 
//	DrvGPIO_Open(E_GPC, 7,  E_IO_OUTPUT);
//    DrvGPIO_SetBit(E_GPC, 6);
//    DrvGPIO_SetBit(E_GPC, 7);

    DrvPWM_Open();  
    DrvGPIO_InitFunction(E_FUNC_PWM0);
    
	DrvPWM_SelectClockSource(DRVPWM_TIMER0, DRVPWM_EXT_12M);

	gPt.u8Mode = DRVPWM_AUTO_RELOAD_MODE;
    gPt.u32Frequency = 5000;         /* 100K Hz */
    gPt.u8HighPulseRatio = 50;      /* High Pulse peroid : Total Pulse peroid = 30 : 100 */ 
    gPt.i32Inverter = 0;    
	DrvPWM_SetTimerClk(DRVPWM_TIMER0, &gPt);

   	DrvPWM_SetTimerIO(DRVPWM_TIMER0, 1);
    DrvPWM_Enable(DRVPWM_TIMER0, 1);  
	DrvPWM_SelectClockSource(DRVPWM_CAP0, DRVPWM_EXT_12M);

	gPt.u8Mode = DRVPWM_AUTO_RELOAD_MODE;
    gPt.u32Frequency = 100;         /* 100K Hz */
    gPt.u8HighPulseRatio = 50;      /* High Pulse peroid : Total Pulse peroid = 30 : 100 */ 
    gPt.u32Duty = 0x10000;          /* Set the counter to the maximum value */
    gPt.i32Inverter = 0;    
	PWMA->PPR.CP01=11;			//Prescaler 0~255, Setting 0 to stop output clock
	PWMA->CSR.CSR0=3;			//clock divider->0:/2, 1:/4, 2:/8, 3:/16, 4:/1
	PWMA->CNR0 = 0x7FFF;

	// Set irq handling
	DrvPWM_EnableInt(DRVPWM_CAP0, DRVPWM_CAP_ALL_INT, CaptureIrqHandle);
	// Disable irq
   	DrvPWM_SetTimerIO(DRVPWM_CAP0, 1);
    DrvPWM_Enable(DRVPWM_CAP0, 1);  
}

